package com.ruoyi.activiti.bpmnparse;

import com.alibaba.fastjson.JSON;
import com.ruoyi.activiti.bpmnparse.x6translator.*;
import org.apache.commons.io.IOUtils;
import org.dom4j.*;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

import java.io.*;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class BpmnParser {
    private String processId;
    private String processName;
    private BpmnHelper helper = new X6Helper();
    private List<Map<String,Object>> bpmnList = new LinkedList<>();
    private Map<String,Translator> translatorMap = new HashMap<>();
    public void initTranslator(List<Map<String,Object>> x6ListMap){
        Translator userTask = new X6UserTaskTranslator(x6ListMap);
        translatorMap.put(userTask.getTaskType(),userTask);
        Translator exclusionGateway = new ExclusionGatewayTranslator(x6ListMap);
        translatorMap.put(exclusionGateway.getTaskType(),exclusionGateway);
        Translator sequenceFlow = new SequenceFlowTranslator(x6ListMap);
        translatorMap.put(sequenceFlow.getTaskType(),sequenceFlow);
        Translator startEvent = new StartEventTranslator(x6ListMap);
        translatorMap.put(startEvent.getTaskType(),startEvent);
        Translator endEvent = new EndEventTranslator(x6ListMap);
        translatorMap.put(endEvent.getTaskType(),endEvent);
        Translator inclusiveTranslator = new InclusiveGatewayTranslator(x6ListMap);
        translatorMap.put(inclusiveTranslator.getTaskType(),inclusiveTranslator);
        Translator parallelGateway = new ParallelGatewayTranslator(x6ListMap);
        translatorMap.put(parallelGateway.getTaskType(),parallelGateway);
//        Translator userMultiTask = new MultiUserTaskTranslator(x6ListMap);
//        translatorMap.put(userMultiTask.getTaskType(),userMultiTask);
    }

    public void setProcessId(String processId) {
        this.processId = processId;
    }

    public void setProcessName(String processName) {
        this.processName = processName;
    }

    public void parse(List<Map<String,Object>> mapList) {
        for (Map<String, Object> map: mapList){
            String type = helper.getBpmnType(map);
            if(type == null) {
                continue;
            }
            Translator translator = translatorMap.get(type);
            if(translator != null) {
                Map<String, Object> bpmnMap = translator.translate(map);
                bpmnList.add(bpmnMap);
            }
        }
    }
    public void generateBpmnXml(String name) throws Exception {
        // 1. 创建document对象
        Document document = DocumentHelper.createDocument();
        Element definitions = addDefinition(document);
        // ==========创建流程根节点<process id="exampleDemo" name="示例Demo" isExecutable="true">=======
        Element process = addProcess(definitions);
        // 3. 创建流程process节点中各个子节点，比如userTask,sequenceFlow等，bpmnList是解析好的bpmn对应的节点
        for (Map<String, Object> bpmnMap : bpmnList) {
            String type = (String) bpmnMap.get(Translator.BPMN_TYPE);
            Element node = process.addElement(type);
            // 添加节点属性信息
            addBpmnAttribute(bpmnMap,node);
        }
        // 4. 设置生成xml的格式，并生产xml文件
        OutputFormat format = OutputFormat.createPrettyPrint();
        format.setEncoding("UTF-8");
        File file = new File(name);
        System.out.println("file = " + file.getAbsolutePath());
        XMLWriter writer = new XMLWriter(new FileOutputStream(file), format);
        // 设置是否转义，默认使用转义字符
        writer.setEscapeText(false);
        writer.write(document);
        writer.flush();
        writer.close();
        System.out.println("生成xml文件成功");
    }
    private Element addProcess(Element element){
        Element process = element.addElement("process");
        // 2. 向process节点中添加属性
        process.addAttribute(Translator.BPMN_ID, processId);
        process.addAttribute(Translator.BPMN_NAME,processName);
        process.addAttribute("isExecutable","true");
        return process;
    }
    private Element addDefinition(Document document){
        // 第二个参数是命名空间，一定要这样加，不然会有问题的。
        Element definition = document.addElement("definitions","http://www.omg.org/spec/BPMN/20100524/MODEL");
        definition.addAttribute("xmlns:activiti","http://activiti.org/bpmn");
        definition.addAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance");
        definition.addAttribute("xmlns:xsd","http://www.w3.org/2001/XMLSchema");
        definition.addAttribute("xmlns:bpmndi","http://www.omg.org/spec/BPMN/20100524/DI");
        definition.addAttribute("xmlns:omgdc","http://www.omg.org/spec/DD/20100524/DC");
        definition.addAttribute("xmlns:omgdi","http://www.omg.org/spec/DD/20100524/DI");
        definition.addAttribute("typeLanguage","http://www.w3.org/2001/XMLSchema");
        definition.addAttribute("expressionLanguage","http://www.w3.org/1999/XPath");
        definition.addAttribute("targetNamespace","http://www.kafeitu.me/activiti/leave");
        return definition;
    }
    private void addBpmnAttribute(Map<String, Object> bpmnMap,Element node){
        for (Map.Entry<String, Object> entry : bpmnMap.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            if(value instanceof Map) {
                Map<String,Object> valueMap = (Map<String, Object>) value;
                Element childNode = node.addElement(key);
                addBpmnAttribute(valueMap,childNode);
            } else if(value instanceof String) {
                String valueStr = (String) value;
                if(Translator.BPMN_VALUE.equals(key)) {
                    node.setText(valueStr);
                } else if(!Translator.BPMN_TYPE.equals(key)) {
                    node.addAttribute(key,valueStr);
                }
            }
        }
    }

    public static void main(String[] args) throws IOException {
        InputStream is = BpmnParser.class.getClassLoader().getResourceAsStream("json/simple.json");
        String x6Json = IOUtils.toString(is, Charset.defaultCharset());
        Map<String,Object> x6Map = (Map<String, Object>) JSON.parse(x6Json);
        List<Map<String,Object>> cellMap = (List<Map<String, Object>>) x6Map.get("cells");
        BpmnParser bpmnParser = new BpmnParser();
        bpmnParser.setProcessId("leave");
        bpmnParser.setProcessName("请假申请");
        bpmnParser.initTranslator(cellMap);
        bpmnParser.parse(cellMap);
        try {
            String name = "multitask.bpmn20.xml";
            bpmnParser.generateBpmnXml(name);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
